# Vue 3

O Vue Next e mais conhecido como Vue 3.x vem com a proposta de agregar maior flexibilidade e utilidades gerais aos projetos Vue, sem mudar drasticamente o Vue.

# Iniciando no Vue-Next

Atualizando o VueCLI, já tera disponível a opção de inicializar o projeto com a versão 3.x.

Em um projeto na versão 2.x já existente, execute o comando:

yarn add vue@next

# Construção da Instância

O nosso querido main.js possui diferenças:

import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

Agora utilizamos o createApp para criar a nossa instância, ao invés do new Vue

Para acrescentar dependências, utilizamos:

// Exemplo utilizando vuex e vue-router
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

const app = createApp(App);

app.use(store);
app.use(router);

app.mount("#app");

# API de Composição

A API de composição é um conjunto de funções aditivas que permite uma melhor utilização em componentes. Veio com a proposta de acrescentar a experiência principalmente em projetos grandes, que exige uma modularidade e flexibilidade dos componentes maior.

A medida que os projetos de Vue 2.x vão aumentando, cada vez mais fica complicado de entender os grandes arquivos .vue do projeto, além da falta de formas de extrair a lógica entre vários componentes devido a limitação dos mixins.

# Estado Reativo

<template>
  <button @click.prevent="increment">
    {{ count }}
  </button>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() { // setup é o antigo created()
    const count = ref(0); // criando uma referencia

    const increment = () => { // criando um método
      count++
    }

    return { // retornando ao template
      count,
      increment
    }
  }
}
</script>
  • o setup() significa a mesma coisa que o antigo hook created(), e podemos retornar o que queremos que o template tenha acesso.

  • Utilizamos o ref para criar uma referência do count, e retornamos o estado.

  • Diferente do Vue 2.x, não temos necessidade de utilizar o this.**.

  • Podemos mutar a variável diretamente pelo template, como: @click="++count"

O setup recebe dois argumentos:

setup(props, context) {
  console.log(props);
  console.log(context);
}

Podemos também formar grupos de objetos com o reactive(anterior no Vue 2.x como Vue.observable)

<script>
import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({
      nome: 'Jorge',
      idade: 20
    })

    const trocarNome = nome => {
      state.nome = nome
    }

    return {
      state,
      trocarNome
    }
  }
}
</script>

# Computed

<template>
  <button @click.prevent="increment">
    {{ total }}
  </button>
</template>

<script>
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count++
    }

    const total = computed(() => 'Total é: ' + count)

    return {
      count,
      total,
      increment
    }
  }
}
</script>

# toRefs

Agora conseguimos criar componentes de formulário de uma forma melhorada, sem a necessidade de utilizar $emit por conta de termos a referência das variáveis.

<template>
  <input v-model="nome">
  <input v-model="idade">
</template>

<script>
import { ref, computed, toRefs } from 'vue'

export default {
  setup(props) { // se desconstruirmos props, perderemos a reatividade.
    // podemos mutar a variável sem problemas, o estado será atualizado no componente pai
    const { nome, idade } = toRefs(props)

    return { nome, idade }
  }
}
</script>

# watch

Mantendo a sintaxe parecido com o Vue 2.x, o primeiro parâmetro é o que será escutado e o segundo um callback com a execução da lógica, recebendo o novo valor como parâmetro do callback:

<script>
// Switch básico de tema
import { defineComponent, ref, watch } from "vue";
import { useDefaultStore } from "@/config"; // Store usando Pinia, explicado em seções futuras
import { JsonWriteFile } from "@/services/fs"; // Serviço básico para fs/path
import { useToast } from "vue-toastification"; // vue-toastification já está pronto para Vue 3.x

export default defineComponent({
  name: "App",
  setup() {
    const toast = useToast(); // Sintaxe de Hook, explicado na terceira seção
    const store = useDefaultStore(); // Sintaxe de Hook, explicado na terceira seção
    const theme = ref(store.base.theme);

    watch(theme, (newTheme) => {
      newTheme === "dark" 
        ? document.querySelector("html").classList.add("dark")
        : document.querySelector("html").classList.remove("dark");

      store.base.theme = newTheme;

      const msg = newTheme === "dark" ? "Escuro" : "Claro";

      toast.success(`Tema ${msg} selecionado!`);

      JsonWriteFile("config/base.json", store.base);
    });

    return { theme }
  }
});
</script>

# watchEffect

O watchEffect escuta todas as alterações que estão no estado reativo:

<template>
  <button @click.prevent="increment">
    {{ total }}
  </button>
</template>

<script>
import { ref, computed, watchEffect } from 'vue'

export default {
  setup() {
    const count = ref(0);

    const increment = () => {
      count++
    }

    const total = computed(() => 'Total é: ' + count)

    watchEffect(() => {
      console.log(count)
    })

    return {
      count,
      total,
      increment
    }
  }
}
</script>

# LifeCycleHooks

Temos os mesmos life cycles, só que agora na API:

<script>
import { onBeforeMounted, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const item = ref({ foo: 'bar' });

    onBeforeMounted(() => {
      console.log('ainda estou montando!');
    }

    onMounted(() => {
      localStorage.setItem('item', JSON.stringify(item.value));
    })
  
    onUnmounted(() => {
      localStorage.removeItem('item');
    })

    return { item }
  }
}
</script>

# Nuxt

O Nuxt introduzou o módulo da API de Composição, dessa forma conseguimos utilizar as novas features em nossos projetos:

// index.vue
import { ref, defineComponent, useContext } from '@nuxtjs/composition-api'

export default defineComponent({
  setup() {
    const item = ref({ foo: 'bar' });
    const { store } = useContext(); // implementação do Vuex
    store.dispatch('action', item.value);
  },
})

Na próxima seção iremos mostrar algumas mudanças para utilizar as lib's no Vue 3.x

Próxima Seção